package com.mybatis.boost.mapper.util.sql;

import tk.mybatis.mapper.entity.EntityColumn;

import static com.mybatis.boost.mapper.SingleBaseMapper.PARAM_DEFAULT;
import static com.mybatis.boost.mapper.SingleBaseMapper.PARAM_FIELD;
import static com.mybatis.boost.mapper.util.OGNL.CHECK_FOR_BLANK;
import static com.mybatis.boost.mapper.util.sql.SqlUtil.*;
import static java.lang.String.format;
import static tk.mybatis.mapper.mapperhelper.EntityHelper.getColumns;
import static tk.mybatis.mapper.mapperhelper.EntityHelper.getPKColumns;

/**
 * @author jearton
 * @since 2017/2/10
 */
public abstract class SelectSqlUtil {

    public static String checkField() {
        String exp = format(CHECK_FOR_BLANK, PARAM_FIELD, "'field must not be blank'");
        return "<if test=\"" + exp + "\"/>\n";
    }

    /**
     * select xxx,xxx...
     *
     * @param entityClass 实体类
     */
    public static String selectAllColumns(Class<?> entityClass) {
        return "SELECT " + _getAllColumns(entityClass) + " ";
    }

    /**
     * select指定列，支持distinct查询
     */
    public static String selectColumnsByCondition(Class<?> entityClass) {
        //不支持指定列的时候查询全部列
        return "SELECT <if test=\"example neq null and example.distinct\">distinct</if>" +
                "<if test=\"example neq null and @tk.mybatis.mapper.util.OGNL@hasSelectColumns(example)\">" +
                "  <foreach collection=\"example.selectColumns\" item=\"selectColumn\" separator=\",\">" +
                "    ${selectColumn}" +
                "  </foreach>" +
                "</if>" +
                "<if test=\"example eq null or @tk.mybatis.mapper.util.OGNL@hasNoSelectColumns(example)\">" +
                _getAllColumns(entityClass) +
                "</if>";
    }

    /**
     * select count(x)
     *
     * @param entityClass 实体类
     */
    public static String selectCount(Class<?> entityClass) {
        return "SELECT COUNT(" +
                getPKColumns(entityClass).stream().findFirst().map(EntityColumn::getColumn).orElse("0") +
                ") ";
    }

    /**
     * from tableName - 动态表名
     *
     * @param entityClass      实体类
     * @param defaultTableName 默认表名
     */
    public static String fromTable(Class<?> entityClass, String defaultTableName) {
        return " FROM " + getDynamicTableName(entityClass, defaultTableName) + " ";
    }

    /**
     * where主键条件
     *
     * @param entityClass 实体类
     */
    public static String wherePKColumns(Class<?> entityClass) {
        return wherePKColumns(entityClass, PARAM_DEFAULT);
    }

    /**
     * where主键条件
     *
     * @param entityClass 实体类
     */
    public static String wherePKColumns(Class<?> entityClass, String parameterName) {
        StringBuilder sql = new StringBuilder();
        sql.append("<where>");
        getPKColumns(entityClass).forEach(column -> sql.append(" AND ").append(getColumnEqualsHolder(parameterName, column)));
        sql.append("</where>");
        return sql.toString();
    }

    /**
     * where单列条件
     */
    public static String whereSingleColumn(Class<?> entityClass) {
        return "<where>" + getSingleColumnPair(entityClass) + "</where>";
    }

    /**
     * Condition查询中的where结构
     */
    public static String conditionWhereClause() {
        return conditionWhereClause(PARAM_DEFAULT);
    }

    /**
     * Condition查询中的where结构
     */
    public static String conditionWhereClause(String parameterName) {
        String exampleEntity = getEntityPrefix(parameterName) + "example";
        return "<where>\n" +
                "  <if test=\"" + exampleEntity + " neq null\">\n" +
                "    <foreach collection=\"" + exampleEntity + ".oredCriteria\" item=\"criteria\" separator=\"or\">\n" +
                "      <if test=\"criteria.valid\">\n" +
                "        <trim prefix=\"(\" prefixOverrides=\"and\" suffix=\")\">\n" +
                "          <foreach collection=\"criteria.criteria\" item=\"criterion\">\n" +
                "            <choose>\n" +
                "              <when test=\"criterion.noValue\">\n" +
                "                and ${criterion.condition}\n" +
                "              </when>\n" +
                "              <when test=\"criterion.singleValue\">\n" +
                "                and ${criterion.condition} #{criterion.value}\n" +
                "              </when>\n" +
                "              <when test=\"criterion.betweenValue\">\n" +
                "                and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +
                "              </when>\n" +
                "              <when test=\"criterion.listValue\">\n" +
                "                and ${criterion.condition}\n" +
                "                <foreach close=\")\" collection=\"criterion.value\" item=\"listItem\" open=\"(\" separator=\",\">\n" +
                "                  #{listItem}\n" +
                "                </foreach>\n" +
                "              </when>\n" +
                "            </choose>\n" +
                "          </foreach>\n" +
                "        </trim>\n" +
                "      </if>\n" +
                "    </foreach>\n" +
                "  </if>\n" +
                "</where>";
    }

    /**
     * Condition查询中的order结构
     */
    public static String conditionOrderByClause() {
        return "<trim prefix=\"ORDER BY\">\n" +
                "  <foreach collection=\"orders\" item=\"order\" separator=\", \">\n" +
                "    ${property} ${direction}" +
                "  </foreach>\n" +
                "</trim>";
    }

    /**
     * Condition查询中的limit结构
     */
    public static String conditionLimitByClause() {
        return getIfNotNull("limit", "limit #{limit.offset}, #{limit.size}");
    }

    /**
     * 获取所有查询列，如id,name,code...
     *
     * @param entityClass 实体类
     */
    private static String _getAllColumns(Class<?> entityClass) {
        return getColumns(entityClass).stream()
                .map(EntityColumn::getColumn)
                .reduce((c1, c2) -> c1 + "," + c2)
                .orElse("");
    }
}
